(function () {

function Node(parent, name, value, attrs) {
    this.parent = parent
    this.nodeName = name
    this.nodeValue = value
    this.children = []
    this.attributes = attrs || {}
};

Node.prototype.up = function (level) {
    if (level) {
        for (var cur = this; cur.parent && level-- > 0; cur = cur.parent) { }

        return cur;
    } else {
        return this.parent || this;
    }
}

Node.prototype.top = function () {
    for (var cur = this; cur.parent; cur = cur.parent) { }

    return cur;
}

Node.prototype.attr = function (name, value) {
    if (value == undefined) {
        return this.attributes[name];
    } else {
        this.attributes[name] = value;

        return this;
    }
};

Node.prototype.attrs = function (attrs) {
    $.each(attrs, function (k, v) {
       this.attributes[k] = v;
    });
}

function Text(parent, text) {
    this.base = Node;
    this.base(parent, '#text', text, {});
}

Text.prototype = new Node;

Text.prototype.text = function (text) {
    if (text == undefined) {
        return this.nodeValue;
    } else {
        this.nodeValue = text;

        return this;
    }
};

Text.prototype.toXml = function() {
    return this.nodeValue;
};

function Element(parent, name, attrs) {
    this.base = Node;
    this.base(parent, name, undefined, attrs);
}

Element.prototype = new Node;

Element.prototype.find = function (name, attrs) {
    $.each(this.children, function (idx, child) {
        if (child.nodeName == name) {
            var matched = true;

            $.each(attrs, function (k, v) {
                matched = child.attr(k) == v;

                return matched;
            });

            if (matched) return child;
        }
    });

    return null;
};

Element.prototype.elem = function (name, attrs) {
    var elem = this.find(name, attrs || {});

    if (!elem) {
        elem = new Element(this, name, attrs || {});

        this.children.push(elem);
    }

    return elem;
};

Element.prototype.text = function (text) {
    if (text == undefined) {
        var str = "";

        $.each(this.children, function (idx, child) {
            str += child.text();
        });

        return str;
    } else {
        this.children = [new Text(this, text)];

        return this;
    }
};

Element.prototype.toXml = function() {
    var str = "<" + this.nodeName;

    $.each(this.attributes, function (k, v) {
        str += " " + k + "='" + v + "'";
    })

    if (this.children.length) {
        str += ">";
        $.each(this.children, function (idx, child) {
            str += child.toXml();
        });
        str += "</" + this.nodeName + ">";
    } else {
        str += "/>";
    }

    return str;
};

function Builder(name, encoding, version) {
    this.base = Element;
    this.base(null, name, {});

    this.version = version || '1.0';
    this.encoding = encoding || 'UTF-8';
}

Builder.prototype = new Element;

Builder.prototype.toXml = function() {
   var str = "<?xml";

   if (this.version) str += " version='" + this.version + "'";
   if (this.encoding) str += " encoding='" + this.encoding + "'";

   str += "?>";

   str += Element.prototype.toXml.call(this);

   return str;
};

if (!window['xmldom']) { window['xmldom'] = {}; }

var xmldom = window['xmldom'];

xmldom.Builder = Builder;

})();